﻿@page "/edit/{ContactId:int}"

@using Microsoft.EntityFrameworkCore;
@implements IDisposable

@inject IDbContextFactory<ContactContext> DbFactory
@inject NavigationManager Nav
@inject IPageHelper PageHelper
@inject EditSuccess EditSuccessState

@if (Contact == null && !Busy)
{
    <p>Could not find contact with id @ContactId.</p>
}
else
{
    <ContactForm Busy="@Busy" Contact="@Contact"
                 DbContact="@DbContact"
                 IsAdd="false"
                 CancelRequest="Cancel"
                 ValidationResult="@(async (success) => await ValidationResultAsync(success))" />
}
@if (ConcurrencyError)
{
    <br />
    <div class="alert alert-danger">
        <p>
            The contact details have changed since it was last loaded. The updated fields are highlighted in the form.
            Please choose an option:
        </p>
        <p><b>Cancel</b> to lose your changes and keep the database version.</p>
        <p><b>Submit</b> to force an update with your version.</p>
    </div>
}
@if (Error)
{
    <br />
    <div class="alert alert-danger">Failed to update the contact (@ErrorMessage).</div>
}

@code {
    /// <summary>
    /// Id of contact to edit
    /// </summary>
    [Parameter]
    public int ContactId { get; set; }

    /// <summary>
    /// The <see cref="ContactContext"/> for database access.
    /// </summary>
    private ContactContext Context { get; set; }

    /// <summary>
    /// Contact being edited
    /// </summary>
    private Contact Contact { get; set; }

    /// <summary>
    /// Database version when concurrency issues exist
    /// </summary>
    private Contact DbContact { get; set; }

    /// <summary>
    /// Avoid concurrent requests
    /// </summary>
    private bool Busy;

    /// <summary>
    /// An error occurred in the update
    /// </summary>
    private bool Error;

    /// <summary>
    /// A concurrency error needs resolution
    /// </summary>
    private bool ConcurrencyError;

    /// <summary>
    /// Error message
    /// </summary>
    private string ErrorMessage = string.Empty;

    /// <summary>
    /// Start it up
    /// </summary>
    /// <returns>Task</returns>
    #region snippet2
    protected override async Task OnInitializedAsync()
    {
        Busy = true;

        try
        {
            Context = DbFactory.CreateDbContext();
            Contact = await Context.Contacts
                .SingleOrDefaultAsync(c => c.Id == ContactId);
        }
        finally
        {
            Busy = false;
        }

        await base.OnInitializedAsync();
    }
    #endregion

    /// <summary>
    /// Result of form validation
    /// </summary>
    /// <param name="success">Success when model is valid</param>
    /// <returns>Task</returns>
    private async Task ValidationResultAsync(bool success)
    {
        if (Busy)
        {
            return;
        }

        if (!success)
        {
            // still need to edit model
            Error = false;
            ConcurrencyError = false;
            return;
        }

        Busy = true; // async
        try
        {
            await Context.SaveChangesAsync();
            EditSuccessState.Success = true;
            // go to view to see the record
            Nav.NavigateTo($"/view/{Contact.Id}");
        }
        catch (DbUpdateConcurrencyException dbex)
        {
            EditSuccessState.Success = false;

            // concurrency issues!
            ConcurrencyError = true;

            // get values from database
            var dbValues = dbex.Entries[0].GetDatabaseValues();

            if (dbValues == null)
            {
                // deleted - show contact not found
                Nav.NavigateTo($"/view/{Contact.Id}");
                return;
            }

            // bind to show labels on form
            DbContact = (Contact)dbValues.ToObject();

            // move to original so second submit works (unless there is another concurrent edit)
            dbex.Entries[0].OriginalValues.SetValues(dbValues);
            Error = false;
            Busy = false;
        }
        catch (Exception ex)
        {
            EditSuccessState.Success = false;
            // unknown exception
            Error = true;
            ErrorMessage = ex.Message;
            Busy = false;
        }
    }

    /// <summary>
    /// Bail out!
    /// </summary>
    private void Cancel()
    {
        Busy = true;
        Nav.NavigateTo($"/{PageHelper.Page}");
    }

    /// <summary>
    /// Implement <see cref="IDisposable"/>.
    /// </summary>
    #region snippet1
    public void Dispose()
    {
        Context.Dispose();
    }
    #endregion
}
